package gitlet;

import java.io.File;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.TreeMap;

import static gitlet.Utils.join;
import static gitlet.Utils.readObject;
import static gitlet.Utils.sha1;
import static gitlet.Utils.writeObject;

/**
 * 储存提交内容的类
 *
 * @author 王子琪、杨凯伟
 * @date 2023/11/16
 */
public class Commit implements Serializable {
    private final String message; // 提交的信息
    private final String timestamp; // 时间戳
    private final String parent;
    private final String parent2;
    // 表示这个commit中存放的blobs的地址
    private final String commitHash;
    // 表示这个commit的父母的地址
    private final TreeMap<String, String> fileToBlob;

    /**
     * 写入要提交的内容
     *
     * @param commitsDir commit目录
     * @param msg        message内容
     * @param parentID   父节点ID
     * @param parent2ID  双亲结点ID
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public Commit(File commitsDir, String msg, String parentID, String parent2ID) {
        fileToBlob = new TreeMap<>();

        if (!parentID.isEmpty()) { // 不是initial commit
            Commit lastCommit = Commit.fromFile(commitsDir, parentID);
            add(lastCommit.getFileToBlob()); // 复制parent commit中的东西
        }
        message = msg;
        parent = parentID;
        parent2 = parent2ID;
        timestamp = getTimeStamp();
        commitHash = generateHash();
    }

    /**
     * 从指定文件中反序列化得到commit
     *
     * @param commitsDir commit目录
     * @param commitName commit名称
     * @return 读取到的commit
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public static Commit fromFile(File commitsDir, String commitName) {
        File file = join(commitsDir, commitName);
        return readObject(file, Commit.class);
    }

    /**
     * 从远程仓库中获取提交的内容
     *
     * @param url        本地地址
     * @param commitName commit名称
     * @return {@link Commit}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public static Commit fromRemoteFile(String url, String commitName) {
        File temp = join(url, "objects/commits", commitName);
        return readObject(temp, Commit.class);
    }

    /**
     * 将传入的TreeMap复制到本实例中
     *
     * @param ftb fileToBlob
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public void add(TreeMap<String, String> ftb) {
        for (String fileName : ftb.keySet()) {
            add(fileName, ftb.get(fileName));
        }
    }

    /**
     * 获取所有文件内容储存文件
     *
     * @return fileToBlob
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public TreeMap<String, String> getFileToBlob() {
        return fileToBlob;
    }

    /**
     * 获取时间戳
     *
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    private String getTimeStamp() {
        String ptn = "EEE MMM d HH:mm:ss yyyy Z";
        DateFormat dateFormat = new SimpleDateFormat(ptn, Locale.US);
        if (parent.isEmpty()) {
            return dateFormat.format(new Date(0));
        } else {
            return dateFormat.format(new Date());
        }
    }

    /**
     * 获取所有内容的SHA1-commitHash
     *
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    private String generateHash() {
        return sha1(message, timestamp, parent, fileToBlob.toString());
    }

    /**
     * 将文件名为fileName的文件加入所有提交文件中
     *
     * @param fileName 文件名称
     * @param blob     添加到Blob
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public void add(String fileName, String blob) {
        fileToBlob.put(fileName, blob);
    }

    /**
     * 返回commit的哈希值
     *
     * @return commit的哈希值
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public String getHash() {
        return commitHash;
    }

    /**
     * 移除提交的文件
     *
     * @param ftb FileToBlob
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public void remove(Set<String> ftb) {
        for (String fileName : ftb) {
            remove(fileName);
        }
    }

    /**
     * 将文件名为fileName的文件从提交文件中删除
     *
     * @param fileName 文件名称
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public void remove(String fileName) {
        fileToBlob.remove(fileName);
    }

    /**
     * 将提交写入文件中
     *
     * @param commitsDir commit
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public void saveCommit(File commitsDir) {
        writeObject(join(commitsDir, commitHash), this);
    }

    /**
     * 输出本次提交的所有信息
     *
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public String toString() {
        return "===" +
                "\ncommit " + commitHash +
                "\nDate: " + timestamp +
                "\n" + message + "\n";
    }

    /**
     * 获取提交的文件的所有内容
     *
     * @param fileName 文件名称
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public String getBlob(String fileName) {
        return fileToBlob.get(fileName);
    }

    /**
     * 所有提交文件名的set集合
     *
     * @return 所有提交文件名的set集合
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public Set<String> getFiles() {
        return fileToBlob.keySet();
    }

    /**
     * 查看commit文件中是否有fileName名的文件
     *
     * @param fileName 文件名称
     * @return ture/false
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public boolean hasFile(String fileName) {
        return fileToBlob.containsKey(fileName);
    }

    /**
     * 找到父commit
     *
     * @return 父commit
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public String getParent() {
        return parent;
    }

    /**
     * 获取父commit(merge后得到)
     *
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public String getParent2() {
        return parent2;
    }

    /**
     * 获取提交时的信息
     *
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public String getMessage() {
        return message;
    }
}
